/*
 *     Prionace - A file navigator to manage your files
 *
 *     Copyright (C) 2012 Francivan Bezerra
 *
 *     This file is part of Prionace.
 *
 *     Prionace is free software: you can redistribute it and/or modify
 *     it under the terms of the GNU General Public License as published by
 *     the Free Software Foundation, either version 3 of the License, or
 *     (at your option) any later version.
 *
 *     Prionace is distributed in the hope that it will be useful,
 *     but WITHOUT ANY WARRANTY; without even the implied warranty of
 *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *     GNU General Public License for more details.
 *
 *     You should have received a copy of the GNU General Public License
 *     along with Prionace.  If not, see <http://www.gnu.org/licenses/>.
 */

package org.fba.prionace.file;

import java.awt.Dimension;
import java.awt.Image;
import java.io.File;
import java.io.IOException;
import java.text.DateFormat;
import java.text.DecimalFormat;
import java.text.Normalizer;

import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JOptionPane;
import javax.swing.JToggleButton;
import javax.swing.SwingConstants;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;

import org.apache.tika.Tika;

public class JFile extends JToggleButton implements ChangeListener,
		Comparable<JFile> {

	private File file;

	private ImageIcon icon;

	private Dimension iconSize;

	private boolean autoSizeable;

	public JFile() {
		super();
		setAutoSizeable(false);
		setIconSize(new Dimension(48, 48));
		setVerticalTextPosition(SwingConstants.BOTTOM);
		setHorizontalTextPosition(SwingConstants.CENTER);
		setContentAreaFilled(isSelected());
		setBorderPainted(false);
		setFocusable(false);
		addChangeListener(this);
	}

	public JFile(File file) {
		this();
		setFile(file);
		createIcon();
		setToolTipText(createToolTipText());
	}

	public JFile(String path) {
		this(new File(path));
	}

	public File getFile() {
		return file;
	}

	public void setFile(File file) {
		setText(file.getName().isEmpty() ? file.getAbsolutePath() : file
				.getName());
		this.file = file;
	}

	public JFile getParentFile(boolean iconVisible) {
		if (file.getParentFile() == null) {
			return null;
		}

		if (iconVisible) {
			return new JFile(file.getParentFile());
		}

		JFile parent = new JFile();
		parent.setFile(file.getParentFile());
		return parent;
	}

	public ImageIcon getIcon() {
		return icon;
	}

	private void createIcon() {
		ImageIcon icon;

		try {
			icon = IconFactory.getInstance().createIcon(getFile(),
					getIconSize());
		} catch (IOException e) {
			icon = null;
		}
		setIcon(icon);
	}

	@Override
	public void setIcon(Icon icon) {
		super.setIcon(icon);
		this.icon = (ImageIcon) icon;
	}

	public void removeIcon() {
		icon.getImage().flush();
		setIcon(null);
		setAutoSizeable(false);
	}

	public Dimension getIconSize() {
		return iconSize;
	}

	public void setIconSize(Dimension iconSize) {
		if (icon != null) {
			icon = new ImageIcon(icon.getImage().getScaledInstance(
					iconSize.width, iconSize.height, Image.SCALE_SMOOTH));
		}
		this.iconSize = iconSize;
	}

	public boolean isAutoSizeable() {
		return autoSizeable;
	}

	public void setAutoSizeable(boolean autoSizeable) {
		if (autoSizeable) {
			setPreferredSize(new Dimension(iconSize.width * 3,
					iconSize.height * 2));
		} else {
			setPreferredSize(null);
		}

		this.autoSizeable = autoSizeable;
	}

	public void rename() {
		String newName = JOptionPane.showInputDialog(this, "Renomear para:",
				getText());
		if (newName != null && !newName.equals(getText())
				&& !newName.contains(File.separator)) {
			File renamed = new File(getFile().getParent()
					.concat(File.separator).concat(newName));
			if (getFile().renameTo(renamed)) {
				setFile(renamed);
				setToolTipText(createToolTipText());
			}
		}
	}

	/**
	 * Deletes the file or directory denoted by the <code>file</code> object's
	 * abstract pathname. If the <code>file</code>'s pathname denotes a
	 * directory, then the entire tree will be deleted.
	 * 
	 * @return <code>true</code> if and only if the file or directory is
	 *         successfully deleted; <code>false</code> otherwise.
	 * 
	 * @throws SecurityException
	 *             If a security manager exists and its
	 *             <code>{@link java.lang.SecurityManager#checkDelete}</code>
	 *             method denies delete access to the file.
	 */
	public boolean delete() throws SecurityException {
		boolean deleted;

		if (getFile().isDirectory()) {
			File[] list = getFile().listFiles();

			if (list.length > 0) {
				for (File f : list) {
					JFile delFile = new JFile(f);
					delFile.delete();
				}
			}
		}

		deleted = getFile().delete();

		if (!deleted) {
			String message = "Impossível remover:<br>";
			message = message.concat(getFile().getAbsolutePath());
			throw new SecurityException(message);
		}

		return deleted;
	}

	public String getHumanReadableSize() {
		DecimalFormat decimalFormat = new DecimalFormat("#0.##");
		double length = getFile().length();
		int unit = (int) (Math.log(getFile().length()) / Math.log(1024));
		String[] units = { " bytes", " KiB", " MiB", " GiB", " TiB", " PiB",
				" EiB" };

		if (unit > 0) {
			length = getFile().length() / Math.pow(1024, unit);
		}

		if (length == 0) {
			unit = 0;
		}

		return decimalFormat.format(length).concat(units[unit]);
	}

	public String getMimeType() {
		String mimeType = null;
		Tika tika = new Tika();

		try {
			mimeType = tika.detect(file);
		} catch (IOException e) {
			mimeType = "unknown";
		}

		return mimeType;
	}

	private String createToolTipText() {
		StringBuffer text = new StringBuffer("<html>");
		DateFormat dateFormat = DateFormat.getDateTimeInstance(
				DateFormat.SHORT, DateFormat.SHORT);

		text.append("<b>Nome:</b> ");
		text.append(getText());

		String lastModified = dateFormat.format(getFile().lastModified());
		text.append("<br>");
		text.append("<b>Última modificação:</b> ");
		text.append(lastModified);

		/*
		 * Tika tika = new Tika(); String mimeType = new String(); try {
		 * mimeType = tika.detect(getFile()); } catch (IOException e) {
		 * e.printStackTrace(); } text.append("<br>");
		 * text.append("<b>Mime type:</b> "); text.append(mimeType);
		 */

		if (getFile().isFile()) {
			text.append("<br>");
			text.append("<b>Tamanho:</b> ");
			text.append(getHumanReadableSize());
		}

		text.append("</html>");

		return text.toString();
	}

	@Override
	public int hashCode() {
		return getFile().hashCode();
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj) {
			return true;
		}

		if (!(obj instanceof JFile)) {
			return false;
		}

		File other = ((JFile) obj).getFile();
		return file.equals(other);
	}

	public int compareTo(JFile file) {
		if (file == null || file.getFile().getName() == null) {
			return 0;
		}

		String thisLower = getFile().getName().toLowerCase();
		String fileLower = file.getFile().getName().toLowerCase();

		thisLower = Normalizer.normalize(thisLower, Normalizer.Form.NFD)
				.replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
		fileLower = Normalizer.normalize(fileLower, Normalizer.Form.NFD)
				.replaceAll("\\p{InCombiningDiacriticalMarks}+", "");

		if (getFile().isDirectory()) {
			thisLower = "[d]" + thisLower;
		} else {
			thisLower = "[f]" + thisLower;
		}

		if (file.getFile().isDirectory()) {
			fileLower = "[d]" + fileLower;
		} else {
			fileLower = "[f]" + fileLower;
		}

		return thisLower.compareTo(fileLower);
	}

	public void stateChanged(ChangeEvent e) {
		setContentAreaFilled(isSelected());
	}
}
